// Author: Ivan Kazmenko (gassa@mail.ru)
// Generator for problem "oasis"
module gen;
import std.algorithm;
import std.conv;
import std.exception;
import std.format;
import std.random;
import std.range;
import std.stdio;

immutable string testFormat = "%03d";
enum generateAnswers = false;

immutable int minN =   1;
immutable int maxN = 100;
immutable int maxD = 100;

struct Test
{
	int [] d;

	string comment;

	@property auto n () const
	{
		return d.length;
	}

	void validate () const
	{
		enforce (minN <= n && n <= maxN);
		enforce (chain (only (0), d).isStrictlyMonotonic);
		enforce (d.back <= maxD);
	}

	void print (File f) const
	{
		f.writeln (n);
		f.writefln ("%(%s %)", d);
	}

	string log () const
	{
		return format ("%s (n = %s)", comment, n);
	}
}

Test [] tests;

void printAllTests ()
{
	foreach (testNumber, test; tests)
	{
		test.validate ();
		auto testString = format (testFormat, testNumber + 1);
		auto f = File (testString, "wt");
		test.print (f);
		static if (generateAnswers)
		{
			auto g = File (testString ~ ".a", "wt");
			test.printAnswer (g);
		}
		writeln (testString, ": ", test.log ());
	}
}

Mt19937 rndLocal;
T rndValue (T) () {return uniform !(T) (rndLocal);}
T rndValue (T) (T lim) {return uniform (cast (T) (0), lim, rndLocal);}
T rndValue (T) (T lo, T hi) {return uniform (lo, hi, rndLocal);}
auto rndChoice (R) (R r) {return r[rndValue (cast (int) (r.length))];}

int [] genRandomSequence (int num, int hi)
{
	int [] res;
	foreach (i; 0..hi)
	{
		if (rndValue (hi - i) < num)
		{
			res ~= i + 1;
			num -= 1;
		}
	}
	return res;
}

void main ()
{
	rndLocal.seed (157_159_003);

	tests ~= Test ([3, 5], "example test 1");
	tests ~= Test ([3, 7, 10], "example test 2");

	tests ~= Test ([100], "small test 1");
	tests ~= Test ([1, 3, 6, 10, 15], "small test 2");
	tests ~= Test ([4, 8, 12, 16], "small test 3");

	tests ~= Test (genRandomSequence (10, 20), "random test 1");
	tests ~= Test (genRandomSequence (15, 100), "random test 2");
	tests ~= Test (genRandomSequence (78, 97), "random test 3");

	tests ~= Test (iota (51, 101, 2).array, "large test 1");
	tests ~= Test (iota (1, 40).array ~ iota (51, 101).array,
	    "large test 2");
	tests ~= Test (iota (1, maxD - 1).array ~ maxD, "large test 3");
	tests ~= Test (iota (1, maxD + 1).array, "large test 4");

	printAllTests ();
}
